#include "mp3.h"

#include <stdint.h>
#include <limits.h>

#define MP3_MASK 0xFFFE0CCF
#define MP3_CHANNEL_MODE_MONO 3
#define MP3_HEADER_LEN 4
typedef struct {
    int frameSize;
    int errorProtection;
    int layer;
    int sampleRate;
    int sampleRateIdx;
    int bitRate;
    int chnCnt;
    int chnMode;
    int modeExtension;
    int lsf;
} Mp3DecodeHeader;

const uint16_t MP3_BITRATE_TABLE[2][3][15] = {
    { {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 },
      {0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384 },
      {0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320 } },
    { {0, 32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256},
      {0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160},
      {0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160}
    }
};

const uint16_t MP3_SAMPLINGRATE_TABLE[3] = { 44100, 48000, 32000 };

static int CheckHeader(uint32_t header)
{
    if ((header & 0xffe00000) != 0xffe00000) {
        return -1;
    }
    if ((header & (3<<19)) == 1<<19) {
        return -1;
    }
    if ((header & (3<<17)) == 0) {
        return -1;
    }
    if ((header & (0xf<<12)) == 0xf<<12) {
        return -1;
    }
    if ((header & (3<<10)) == 3<<10) {
        return -1;
    }

    return 0;
}

static int DecodeHeader(uint32_t header, Mp3DecodeHeader *decodedHeader)
{
    int sampleRate, frameSize, mpeg25, padding;
    int sampleRateIdx, bitrate_index;

    int ret = CheckHeader(header);
    if (ret < 0) {
        return ret;
    }

    if (header & (1<<20)) {
        decodedHeader->lsf = (header & (1<<19)) ? 0 : 1;
        mpeg25 = 0;
    } else {
        decodedHeader->lsf = 1;
        mpeg25 = 1;
    }

    decodedHeader->layer = 4 - ((header >> 17) & 3);
    /* extract frequency */
    sampleRateIdx = (header >> 10) & 3;
    if (sampleRateIdx >= 3) {
        sampleRateIdx = 0;
    }
    sampleRate = MP3_SAMPLINGRATE_TABLE[sampleRateIdx] >> (decodedHeader->lsf + mpeg25);
    sampleRateIdx += 3 * (decodedHeader->lsf + mpeg25);
    decodedHeader->sampleRateIdx = sampleRateIdx;
    decodedHeader->errorProtection = ((header >> 16) & 1) ^ 1;
    decodedHeader->sampleRate = sampleRate;

    bitrate_index = (header >> 12) & 0xf;
    padding = (header >> 9) & 1;
    decodedHeader->chnMode = (header >> 6) & 3;
    decodedHeader->modeExtension = (header >> 4) & 3;

    if (decodedHeader->chnMode == MP3_CHANNEL_MODE_MONO) {
        decodedHeader->chnCnt = 1;
    } else {
        decodedHeader->chnCnt = 2;
    }

    if (bitrate_index != 0) {
        frameSize = MP3_BITRATE_TABLE[decodedHeader->lsf][decodedHeader->layer - 1][bitrate_index];
        decodedHeader->bitRate = frameSize * 1000;
        switch(decodedHeader->layer) {
        case 1:
            frameSize = (frameSize * 12000) / sampleRate;
            frameSize = (frameSize + padding) * 4;
            break;
        case 2:
            frameSize = (frameSize * 144000) / sampleRate;
            frameSize += padding;
            break;
        default:
            frameSize = (frameSize * 144000) / (sampleRate << decodedHeader->lsf);
            frameSize += padding;
            break;
        }
        decodedHeader->frameSize = frameSize;
    } else {
        /* if no frame size computed, signal it */
        return 1;
    }

    return 0;
}

static int GetNextFrameSize(uint8_t *buf, uint32_t len, uint32_t *frameSize)
{
    Mp3DecodeHeader mp3DecodeHeader = { 0 };

    if (len <= MP3_HEADER_LEN) {
        return -1;
    }

    // MP3 frame sync flag occupy 11 bits, and all bit is set to 1
    uint32_t header = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
    if (DecodeHeader(header, &mp3DecodeHeader) != 0) {
        return -1;
    }

    *frameSize = mp3DecodeHeader.frameSize;
    return 0;
}

int32_t MP3_Open(Mp3Handle *mp3, const char *streamFilePath)
{
    return Stream_Open(&mp3->stream, streamFilePath, 2048);
}

int32_t MP3_ReadFrame(Mp3Handle *mp3, uint8_t **frame, uint32_t *frameLen)
{
    fseek(mp3->stream.pFile, mp3->stream.u32FileReadOffset, SEEK_SET);
    memset(mp3->stream.pu8BufHeader, 0x00, mp3->stream.u32BufLen);
    uint32_t readLen = fread(mp3->stream.pu8BufHeader, 1, mp3->stream.u32BufLen,
                             mp3->stream.pFile);
    if (readLen <= 0) {
        printf("MP3_ReadFrame read file failed\n");
        mp3->stream.u32FileReadOffset = 0;
        return -1;
    }

    mp3->stream.u32AvailReadLen = readLen;

    if (GetNextFrameSize(mp3->stream.pu8BufHeader, mp3->stream.u32AvailReadLen, frameLen) != 0) {
        perror("mp3 read EOF");
        mp3->stream.u32FileReadOffset = 0;
        return -1;
    }

    *frame = mp3->stream.pu8BufHeader;
    mp3->stream.u32FileReadOffset += *frameLen;
    return 0;
}

int32_t MP3_Close(Mp3Handle *mp3)
{
    return Stream_Close(&mp3->stream);
}

int32_t MP3_Reset(Mp3Handle *mp3)
{
    return Stream_Reset(&mp3->stream);
}

int32_t MP3_GetDecodeInfo(const char *streamPath, uint32_t len, Mp3DecodeInfo *decInfo)
{
    Mp3Handle mp3 = { 0 };
    FILE* mp3File = NULL;
    char aszResolvedpath[PATH_MAX + 1] = { 0 };
    uint8_t mp3Header[MP3_HEADER_LEN] = { 0 };

    if (len == 0 || decInfo == NULL) {
        printf(" input param is illegal\n");
        return -1;
    }

    if (realpath(streamPath, aszResolvedpath) == NULL) {
        printf(" real path fail %s\n", streamPath);
        return -1;
    }

    mp3File = fopen(aszResolvedpath, "r");
    if (mp3File == NULL) {
        printf(" fopen path fail %s\n", aszResolvedpath);
        return -1;
    }

    uint32_t readLen = fread(mp3Header, 1, MP3_HEADER_LEN, mp3File);
    if (readLen <= 0) {
        printf("fread file failed\n");
        fclose(mp3File);
        mp3File = NULL;
        return -1;
    }
    fclose(mp3File);
    mp3File = NULL;

    uint32_t header = (mp3Header[0] << 24) + (mp3Header[1] << 16) + (mp3Header[2] << 8) + mp3Header[3];
    Mp3DecodeHeader decodedHeader = { 0 };

    if (DecodeHeader(header, &decodedHeader) != 0) {
        printf(" DecodeHeader mp3 header fail %x\n", header);
        return -1;
    }

    decInfo->bitRate = decodedHeader.bitRate;
    decInfo->chnCnt = decodedHeader.chnCnt;
    decInfo->samplePerFrame = (decodedHeader.sampleRate * 8 * decodedHeader.frameSize) / decodedHeader.bitRate;
    decInfo->sampleRate = decodedHeader.sampleRate;
    printf("[bitrate:%u][chnCnt:%u][samplePerFrame:%u][sampleRate:%u]\n",
        decInfo->bitRate, decInfo->chnCnt, decInfo->samplePerFrame, decInfo->sampleRate);
    return 0;
}

